package com.glsc.ngateway.platform.service.gitee;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.glsc.ngateway.common.api.common.dto.PageQueryParamDto;
import com.glsc.ngateway.common.api.common.utils.CommonUtils;
import com.glsc.ngateway.common.api.common.utils.DateUtil;
import com.glsc.ngateway.common.api.common.utils.JsonUtil;
import com.glsc.ngateway.common.api.common.utils.PageUtil;
import com.glsc.ngateway.common.api.flowable.dto.CreatedTaskInfoDto;
import com.glsc.ngateway.common.api.flowable.dto.CurrentTaskInfoDto;
import com.glsc.ngateway.common.api.flowable.dto.demand.*;
import com.glsc.ngateway.common.api.flowable.feign.IFeignFlowableService;
import com.glsc.ngateway.common.api.flowable.resp.FlowableResponse;
import com.glsc.ngateway.common.api.flowable.resp.ResponseSituationDto;
import com.glsc.ngateway.common.api.platform.dto.gitee.*;
import com.glsc.ngateway.common.api.platform.dto.gitee.issueBase.IssueFileDto;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.gitee.GiteeSyncLog;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.gitee.IssueBase;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.GiteeSyncLogRepo;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.IssueBaseRepo;
import com.glsc.ngateway.platform.controller.dev.DemandManagementController;
import com.glsc.ngateway.platform.service.it.DictionaryService;
import com.glsc.ngateway.platform.service.system.SystemMainService;
import com.glsc.ngateway.platform.utils.report.ReportFileConfigEnum;
import com.glsc.ngateway.platform.utils.report.ReportFileUtil;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.FileSystemResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author zhengyr
 * @date 2024/1/23-10:51
 * 获取gitee任务相关
 */
@Service
public class GiteeService {
    private static final Logger logger = LoggerFactory.getLogger(GiteeService.class);
    private static final String timeZoneFormatter = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
    private static final String[] closeStateIdArray = {"10", "18", "11", "12", "13"}; //已完成，已取消，已拒绝，已关闭,已验收 的任务状态
    private static final Map<Integer, String> programMap = ImmutableMap.of(34, "国联技术服务平台",17, "资管产品系统",35, "资管直销柜台客户服务平台");
    @Value("${gitee.url}")
    public String baseUrl;

    @Value("${gitee.access_token}")
    public String accessToken;

    @Resource
    private RestTemplate restTemplate;
    @Resource
    private IFeignFlowableService feignDemandService;
    @Resource
    private SystemMainService systemMainService;
    @Resource
    private GiteeSyncLogRepo giteeSyncLogRepo;
    @Resource
    private DemandManagementController managementController;
    @Resource
    private IssueBaseRepo issueBaseRepo;
    @Resource
    private DictionaryService dictionaryService;

    //    @Scheduled(cron = "0 0 8,18 * * ?")
    public void syncAllIssueScheduled() {
        logger.info("开始同步Gitee任务");
        syncAllIssue(LocalDate.now().minusDays(1));
    }

    private ExecutorService demandPool = Executors.newCachedThreadPool();

    /**
     * 同步所有Gitee任务至网关。
     * @param date 用于筛选问题的创建日期，仅同步大于该日期的问题。如果为null，则清空当前所有问题数据并重新同步所有问题。
     */
    public synchronized void syncAllIssue(LocalDate date) {
        int pageSize = 100;
        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(baseUrl + "issues")
                .queryParam("per_page", pageSize)
                .queryParam("access_token", accessToken);
        if (Objects.nonNull(date)) {
            uriBuilder.queryParam("created_at", ">" + date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "T00:00:00.000+08:00");
        } else {
            issueBaseRepo.truncate();
        }
        for (int i = 1; ; i++) {
            String uriString = cn.hutool.core.net.URLDecoder.decode(uriBuilder
                    .queryParam("page", i)
                    .toUriString(), StandardCharsets.UTF_8);
            ResponseEntity<IssueOutDto> resp = restTemplate.getForEntity(uriString, IssueOutDto.class);
            IssueOutDto issueInfo = resp.getBody();
            if (null == issueInfo || issueInfo.getTotal_count() == 0 || CollectionUtil.isEmpty(issueInfo.getData())) {
                logger.info("同步Gitee工作项{}: 同步结束, total:{}", Objects.nonNull(date) ? date : "All", Objects.isNull(issueInfo) ? "接口返回为null" : issueInfo.getTotal_count());
                break;
            }
            List<IssueBase> domainList = issueInfo.getData().stream()
                    .map(dto -> IssueBase.builder()
                            .id(dto.getId())
                            .root_id(dto.getRoot_id())
                            .parent_id(dto.getParent_id())
                            .program_id(dto.getProgram_id())
                            .program_name(systemMainService.findNameByGiteeId(dto.getProgram_id()))
                            .ident(dto.getIdent())
                            .title(dto.getTitle())
                            .issue_state_id(dto.getIssue_state_id())
                            .state(dto.getState())
                            .priority(dto.getPriority())
                            .priority_human(dto.getPriority_human())
                            .assignee(Objects.isNull(dto.getAssignee()) ? null : dto.getAssignee().getUsername())
                            .issue_types(dto.getIssue_type().getTitle())
                            .collaborators(CollectionUtils.isEmpty(dto.getCollaborators()) ? null : dto.getCollaborators().stream().map(GiteeMemberDto::getUsername).collect(Collectors.joining(",")))
                            .plan_started_at(dto.getPlan_started_at())
                            .deadline(dto.getDeadline())
                            .started_at(dto.getStarted_at())
                            .finished_at(dto.getFinished_at())
                            .created_at(dto.getCreated_at())
                            .updated_at(dto.getUpdated_at())
                            .overDays(dto.getOverDays())
                            .build())
                    .collect(Collectors.toList());
            issueBaseRepo.saveAll(domainList);
            logger.info("同步Gitee工作项All: 成功, pageNum-{}, pageSize-{}, 共{}个", i, pageSize, issueInfo.getTotal_count());
        }
    }

    /**
     * 需求模块任务与Gitee同步。
     *
     */
    public void syncGiteeIssue() {
        List<GiteeSyncLog> proIssue = giteeSyncLogRepo.findAllByProcessDefKeyAndTaskDefKeyAndStatusNotIn(
                "DemandManagement", "arrangeTask", List.of("已验收", "已拒绝", "异常或已删除"));
        Map<String, List<OnlineTaskReq>> onlineParamMap = new HashMap<>();
        Set<String> unFinishProcessIdSet = new HashSet<>();
        proIssue.stream()
                .collect(Collectors.groupingBy(GiteeSyncLog::getProcessId))
                .entrySet()
                .forEach(entry -> {
                    String processId = entry.getKey();
                    entry.getValue().forEach(log -> {
                        String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl + "issues/" + log.getGiteeIssueId())
                                .queryParam("access_token", accessToken)
                                .toUriString();
                        try {
                            ResponseEntity<IssueBaseDto> resp = restTemplate.getForEntity(uriString, IssueBaseDto.class);
                            IssueBaseDto issueInfo = resp.getBody();
                            if ((HttpStatus.OK).equals(resp.getStatusCode()) && Objects.nonNull(issueInfo)) {
                                String status = issueInfo.getIssue_state().getTitle();
                                log.setStatus(status);
                                switch (status) {
                                    case "已完成":
                                    case "已验收":
                                        onlineParamMap.compute(processId, (proId, paramList) -> {
                                            ZonedDateTime defaultTime = ZonedDateTime.of(2024,02,01,00,00,00,00,ZoneId.systemDefault());
                                            paramList = CollectionUtils.isEmpty(paramList) ? new ArrayList<>() : paramList;
                                            paramList.add(OnlineTaskReq.builder()
                                                    .rqId(processId)
                                                    .taskId(log.getTaskId())
                                                    .onlineinfo(OnlineInf.builder()
                                                            .actualOnlineDate(StrUtil.isEmpty(issueInfo.getFinished_at()) ? defaultTime.toLocalDate() : ZonedDateTime.parse(issueInfo.getFinished_at()).toLocalDate())
                                                            .devUserId(issueInfo.getAssignee().getUsername())
                                                            .endDevDate(StrUtil.isEmpty(issueInfo.getDeadline()) ? defaultTime.toLocalDate() : ZonedDateTime.parse(issueInfo.getDeadline()).toLocalDate())
                                                            .startDevDate(StrUtil.isEmpty(issueInfo.getPlan_started_at()) ? defaultTime.toLocalDate() : ZonedDateTime.parse(issueInfo.getPlan_started_at()).toLocalDate())
                                                            .build())
                                                    .commnet("Gitee端同步")
                                                    .build());
                                            return paramList;
                                        });
                                        break;
                                    //TODO: 拒绝情况
                                    default:
                                        log.setStatus(status);
                                        unFinishProcessIdSet.add(processId);
                                }
                            }
                        } catch (HttpClientErrorException e) {
                            logger.error("Gitee获取任务详情失败{}", e.getMessage());
                            log.setStatus("异常或已删除");
                        }
                    });
                });
        if (!onlineParamMap.isEmpty()) {
            onlineParamMap.forEach((proId, paramList) -> {
                paramList.forEach(param -> {
                    managementController.onlineTask(null, param);
                    logger.info("Gitee同步需求{}", param);
                });
                if (!unFinishProcessIdSet.contains(proId)) {
                    List<GiteeSyncLog> demandLogs = giteeSyncLogRepo.findAllByProcessIdAndGiteeIssueType(proId, "需求");
                    demandLogs.forEach(log -> {
                        try {
                            HttpHeaders headers = new HttpHeaders();
                            headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                            Map<String, Object> requestBody = new HashMap<>();
                            requestBody.put("access_token", accessToken);
                            requestBody.put("issue_state_id", 10);
                            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
                            restTemplate.put(baseUrl + "issues/" + log.getGiteeIssueId(), requestEntity);
                            log.setStatus("已完成");
                            proIssue.add(log);
                        } catch (HttpClientErrorException e) {
                            logger.error("Gitee修改需求状态失败:{}", e.getMessage());
                        }
                    });
                }
            });
            giteeSyncLogRepo.saveAll(proIssue);
        } else {
            logger.info("Gitee无任务同步");
        }
    }

    @Cacheable(cacheNames = "getGiteeUserId#600")
    public String getGiteeUserId(@NotBlank String account) throws UnsupportedEncodingException {
        String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl + "members")
                .queryParam("access_token", accessToken)
                .queryParam("search", account)
                .toUriString();
        ResponseEntity<GiteeMemberOutDto> resp = restTemplate.getForEntity(URLDecoder.decode(uriString, "UTF-8"), GiteeMemberOutDto.class);
        if (!(HttpStatus.OK).equals(resp.getStatusCode()) || CollectionUtil.isEmpty(resp.getBody().getData())) {
            logger.error("Gitee同步失败：获取成员{" + account + "}失败:" + resp);
            return null;
        }
        return resp.getBody().getData().get(0).getId();
    }

    /**
     * 添加Gitee任务
     */
    public void addGiteeTask(ArrangeTaskReq arrangeTaskReq) {
        demandPool.submit(() -> {
            FlowableDemandDto demandInfo = feignDemandService.getDemandInfo(arrangeTaskReq.getRqId()).getData();
            String sysid = systemMainService.findProgramBySysid(demandInfo.getSystemId());
            if (Strings.isBlank(sysid)) {
                logger.error("Gitee添加任务失败：未找到项目sysid-{}", demandInfo.getSystemId());
                return;
            }
            ResponseSituationDto processInfo = feignDemandService.getProcessInfo(arrangeTaskReq.getRqId()).getData();
            Optional.ofNullable(giteeSyncLogRepo.findAllByProcessIdAndTaskDefKey(arrangeTaskReq.getRqId(), "confirmTask")).stream()
                    .flatMap(Collection::stream)
                    .findFirst()
                    .ifPresent(log -> {
                        CurrentTaskInfoDto currentTask = processInfo.getCurrentTaskInfoDtoList().get(0);
                        Map<String, String> taskIdMap = new HashMap<>();
                        for (int i = 0; i < currentTask.getCurrentAssigneeList().size(); i++) {
                            taskIdMap.put(currentTask.getCurrentAssigneeList().get(i), currentTask.getCurrentTaskIdList().get(i));
                        }
                        arrangeTaskReq.getArrangeinfo().forEach(arrangeInf -> {
                            try {
                                String giteeUserId = getGiteeUserId(arrangeInf.getDevUserId());
                                String giteeDemandId = log.getGiteeIssueId();
                                LocalDateTime startTime = LocalDateTime.parse(processInfo.getStartTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                                LocalDateTime ddl = arrangeInf.getEstiOnlineDate().atStartOfDay();
                                HttpHeaders headers = new HttpHeaders();
                                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                                Map<String, Object> requestBody = new HashMap<>();
                                requestBody.put("access_token", accessToken);
                                requestBody.put("title", startTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")) + "-"
                                        + Optional.ofNullable(dictionaryService.findByDictEntryAndDictSubentry("demand_type", demandInfo.getDemandType()))
                                        .map(d -> "【" + d.getDictValue() + "】").orElse("")
                                        + processInfo.getProcessTitle() + "开发及测试-From" + processInfo.getStartUserChineseName());
                                requestBody.put("description",
                                        "<div><strong><h3>任务描述</h3><strong></div>"
                                                + "<div>" + (Strings.isNotBlank(arrangeInf.getTaskDesc()) ? arrangeInf.getTaskDesc() : Strings.EMPTY) + "</div>"
                                );
                                requestBody.put("assignee_id", giteeUserId);
                                //任务
                                requestBody.put("issue_type_id", "2");
                                requestBody.put("program_id", sysid);
                                requestBody.put("plan_started_at", ZonedDateTime.of(startTime, ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("E MMM dd HH:mm:ss zzz yyyy", Locale.US)));
                                requestBody.put("deadline", ZonedDateTime.of(ddl, ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("E MMM dd HH:mm:ss zzz yyyy", Locale.US)));
                                requestBody.put("priority", Objects.isNull(demandInfo.getDemandLevel()) || demandInfo.getDemandLevel().equals("1") ? 0 : Integer.valueOf(demandInfo.getDemandLevel()));
                                requestBody.put("link_issue_id", giteeDemandId);
                                requestBody.put("link_ref_type", "normal");
                                requestBody.put("link_direction", "none");
                                HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
                                ResponseEntity<IssueBaseDto> giteeResp = restTemplate.postForEntity(baseUrl + "issues", requestEntity, IssueBaseDto.class);
                                if ((HttpStatus.OK).equals(giteeResp.getStatusCode()) || (HttpStatus.CREATED).equals(giteeResp.getStatusCode())) {
                                    IssueBaseDto issueBaseDto = giteeResp.getBody();
                                    giteeSyncLogRepo.save(GiteeSyncLog.builder()
                                            .processDefKey(processInfo.getProcessDefineKey())
                                            .processId(processInfo.getProcessId())
                                            .taskDefKey("arrangeTask")
                                            .taskId(taskIdMap.get(arrangeInf.getDevUserId()))
                                            .status("已创建")
                                            .giteeIssueId(String.valueOf(issueBaseDto.getId()))
                                            .giteeIssueType("任务")
                                            .build());
                                }
                            } catch (UnsupportedEncodingException e) {
                                logger.error("Gitee创建任务失败, {}", arrangeInf, e);
                            }
                        });
                    });
        });
    }

    /**
     * 添加Gitee需求
     */
    public void addGiteeDemand(ResponseSituationDto processInfoParam, ConfirmTaskReq confirmTaskParam) {
        final ResponseSituationDto[] processInfo = {processInfoParam};
        demandPool.submit(() -> {
            FlowableResponse<FlowableDemandDto> demandResp = feignDemandService.getDemandInfo(processInfo[0].getProcessId());
            FlowableDemandDto demandInfo = demandResp.getData();
            String sysid = systemMainService.findProgramBySysid(demandInfo.getSystemId());
            if (Strings.isBlank(sysid)) {
                logger.error("Gitee添加需求失败：未找到项目sysid-{}", demandInfo.getSystemId());
                return;
            }
            processInfo[0] = feignDemandService.getProcessInfo(confirmTaskParam.getRqId()).getData();
            List<CreatedTaskInfoDto> confirmTask = processInfo[0].getCreatedTaskInfoDtoList().stream().filter(t -> t.getTaskDefinitionKey().equals("confirmTask")).collect(Collectors.toList());
            CreatedTaskInfoDto confirmTaskInfo = confirmTask.get(0);
            String userid = null;
            try {
                userid = getGiteeUserId(confirmTaskInfo.getAssignee());
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            LocalDateTime startTime = LocalDateTime.parse(processInfo[0].getStartTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("access_token", accessToken);
//            requestBody.put("title", startTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")) + "-" + processInfo[0].getProcessTitle() + "-From" + processInfo[0].getStartUserChineseName());
            requestBody.put("title", startTime.format(DateTimeFormatter.ofPattern("yyyyMMdd")) + "-"
                    + Optional.ofNullable(dictionaryService.findByDictEntryAndDictSubentry("demand_type", demandInfo.getDemandType()))
                    .map(d -> "【" + d.getDictValue() + "】").orElse("") + "-"
                    + processInfo[0].getProcessTitle() + "-From" + processInfo[0].getStartUserChineseName());
            requestBody.put("description",
                    "<div><strong><h3>任务描述</h3><strong></div>"
                            + "<div>" + (Strings.isNotBlank(demandInfo.getDemandDetail()) ? demandInfo.getDemandDetail() : Strings.EMPTY) + "</div>"
                            + "<div><strong><h3>解决方案</h3><strong></div>"
                            + "<div>" + (Strings.isNotBlank(demandInfo.getSolutionGeneral()) ? demandInfo.getSolutionGeneral() : Strings.EMPTY) + "</div>"
            );
            requestBody.put("assignee_id", userid);
            //需求
            requestBody.put("issue_type_id", "3");
            requestBody.put("program_id", sysid);
            requestBody.put("plan_started_at", ZonedDateTime.of(startTime, ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("E MMM dd HH:mm:ss zzz yyyy", Locale.US)));
            requestBody.put("priority", Objects.isNull(demandInfo.getDemandLevel()) || demandInfo.getDemandLevel().equals("1") ? 0 : Integer.valueOf(demandInfo.getDemandLevel()));
            HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
            ResponseEntity<IssueBaseDto> giteeResp = restTemplate.postForEntity(baseUrl + "issues", requestEntity, IssueBaseDto.class);
            if ((HttpStatus.OK).equals(giteeResp.getStatusCode()) || (HttpStatus.CREATED).equals(giteeResp.getStatusCode())) {
                IssueBaseDto issueBaseDto = giteeResp.getBody();
                giteeSyncLogRepo.save(GiteeSyncLog.builder()
                        .processDefKey(processInfo[0].getProcessDefineKey())
                        .processId(processInfo[0].getProcessId())
                        .taskDefKey(confirmTaskInfo.getTaskDefinitionKey())
                        .taskId(processInfo[0].getCurrentTaskId())
                        .status("已创建")
                        .giteeIssueId(String.valueOf(issueBaseDto.getId()))
                        .giteeIssueType("需求")
                        .build());
                List<Map<String, String>> docDtoList = new ArrayList<>();
                //需求文档
                if (Strings.isNotBlank(demandInfo.getDemandDocument())) {
                    List<Map<String, String>> demandDoc = (List<Map<String, String>>) JsonUtil.str2Obj(demandInfo.getDemandDocument(), new ArrayList<Map<String, String>>().getClass());
                    if (CollectionUtils.isNotEmpty(demandDoc)) {
                        docDtoList.addAll(demandDoc);
                    }
                }
                //设计文档
                if (CollectionUtils.isNotEmpty(confirmTaskParam.getSolutionFiles())) {
                    docDtoList.addAll(confirmTaskParam.getSolutionFiles());
                }
                //上传附件
                if (CollectionUtil.isEmpty(docDtoList)) {
                    logger.info("需求无文档，无需创建");
                } else {
                    List<File> docFiles = Collections.emptyList();
                    try {
                        CountDownLatch lock = new CountDownLatch(docDtoList.size());
                        logger.info("需求文档共{}个，开始创建", docDtoList.size());
//                    MultipartEntityBuilder
                        UUID uuid = UUID.randomUUID();
                        docFiles = docDtoList.parallelStream()
                                .map(doc -> {
                                    byte[] body = feignDemandService.oaDownloadAppendix(doc.get("annexId")).getBody();
                                    String annexName = doc.getOrDefault("annexName", "附件");
                                    File tempFile = null;
                                    FileOutputStream fos = null;
                                    try {
                                        tempFile = new File(uuid + "/" + annexName);
                                        tempFile.getParentFile().mkdirs(); // 如果父目录不存在，则创建
                                        tempFile.createNewFile();
                                        fos = new FileOutputStream(tempFile);
                                        fos.write(body);
                                        fos.flush();
                                        if(Objects.nonNull(fos)) {
                                            fos.close();
                                        }
                                        logger.info("下载附件成功：", annexName);
                                    } catch (IOException e) {
                                        logger.error("下载附件失败：", e.getMessage());
                                    } finally {
                                        lock.countDown();
//                                    tempFile.delete();
//                                    return new FileSystemResource(tempFile);
                                    }
                                    return tempFile;
                                })
                                .filter(Objects::nonNull)
                                .collect(Collectors.toList());
                        lock.await();
                        logger.info("需求文档共{}个，创建完成，下载结果{}个", docDtoList.size(), docFiles.size());
                        if (CollectionUtils.isEmpty(docFiles)) {
                            return;
                        }
                        headers = new HttpHeaders();
                        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
                        MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
                        formData.add("attach_type", "issue");
                        formData.add("attach_id", String.valueOf(issueBaseDto.getId()));
                        HttpEntity<MultiValueMap<String, Object>> requestEntity2 = new HttpEntity<>(formData, headers);
                        docFiles.forEach(doc -> {
                            formData.remove("file");
                            formData.add("file", new FileSystemResource(doc));
                            ResponseEntity<String> docResp = restTemplate.postForEntity(baseUrl + "attach_files/upload?access_token=" + accessToken, requestEntity2, String.class);
                            logger.info("需求管理，文档上传:{}, resp:{}", doc, docResp);
                        });
                        logger.info("需求文档共{}个，全部上传完毕", docFiles.size());
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException(e);
                    } finally {
                        if (CollectionUtils.isNotEmpty(docFiles)) {
                            File parent = docFiles.get(0).getParentFile();
                            docFiles.forEach(File::delete);
                            parent.delete();
                        }
                    }
                }
            }
        });
    }

    public String getWeekDate(int weekNo, LocalDate week) {
        String start = DateUtil.getStr_yyyy_MM_dd(week);
        String end = DateUtil.getStr_yyyy_MM_dd(week.plus(6, ChronoUnit.DAYS));
        return "第" + weekNo + "周（" + start + "-" + end + ")";
    }

    /**
     * 查询周报列表
     *
     * @param deadline 周报结束日，格式yyyy-MM-dd，默认为当年
     *
     */
    public ResponseEntity<byte[]> exportGroupList(String assigneeId, String assigneeName,String deadline) throws IOException {

        LocalDate end = LocalDate.now();
        if (StrUtil.isNotBlank(deadline)) {
            end = LocalDate.parse(deadline);
        }

        int year = end.getYear();
        LocalDate start = LocalDate.of(year, 1, 1);
        ArrayList<IssueExportDto> result = new ArrayList<>();
        List<LocalDate> weekList = DateUtil.splitByWeek(start, end);
        SearchIssueParamDto params = SearchIssueParamDto.builder().assigneeId(assigneeId).build();
        for (int i = 0; i < weekList.size(); i++) {
            LocalDate week = weekList.get(i);
            String weekStart = DateUtil.getStr_yyyy_MM_dd(week);
            String weekEnd = DateUtil.getStr_yyyy_MM_dd(week.plus(6, ChronoUnit.DAYS));
            params.setPlan_started_at(weekStart);
            params.setDeadline(weekEnd);
            List<IssueExportDto> tempList = queryIssueExportList(params, i + 1, week);
            result.addAll(tempList);
            if (tempList.size() < 1) {
                //这一周没有任务，提示手动添加
                result.add(IssueExportDto.builder().week(getWeekDate(i + 1, week)).title("请添加").build());
            }
        }

        HttpHeaders headers = new HttpHeaders();
        String fileName = year + "年-" + assigneeName + "-周报";
        byte[] bytes = ReportFileUtil.export(result, ReportFileConfigEnum.GITEE_WEEK_EXPORT, workbook -> {
            Sheet sheet0 = workbook.getSheetAt(0);
            int upNum = 1, downNum = 1;
            String lastWeek = null;
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            for (; downNum <= sheet0.getLastRowNum(); downNum++) {
                sheet0.getRow(downNum).getCell(0).setCellStyle(cellStyle);
                if (Strings.isBlank(lastWeek)) {
                    lastWeek = sheet0.getRow(downNum).getCell(0).getStringCellValue();
                } else if (!lastWeek.equals(sheet0.getRow(downNum).getCell(0).getStringCellValue())) {
                    if (downNum - upNum > 1) {
                        sheet0.addMergedRegion(new CellRangeAddress(upNum, downNum - 1, 0, 0));
                    }
                    upNum = downNum;
                    lastWeek = sheet0.getRow(downNum).getCell(0).getStringCellValue();
                }
            }
            if (downNum - upNum > 1) {
                sheet0.addMergedRegion(new CellRangeAddress(upNum, downNum, 0, 0));
            }
        });
        headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
        headers.add("Access-Control-Expose-Headers", "Content-Disposition");
        return new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
    }

    /**
     * 按人员分组查询周报格式，不分页
     *
     * @param params
     * @return
     */
    public List<IssueOutVo> getGroupMapList(SearchIssueParamDto params) {
        return getGroupListByAssignee(queryIssueList(params));
    }

    /**
     * 普通查询任务列表
     *
     * @param params
     * @return
     */
    public List<IssueBaseDto> queryIssueList(SearchIssueParamDto params) {
        try {
            SearchIssueParamDto.IssueV2FilterDto issueV2FilterDto = params.buildIssuseV2FilterDto();
            String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl + "issues/")
                    .queryParam("access_token", accessToken)
                    .queryParam("issue_state_ids", issueV2FilterDto.getIssue_state_ids())
                    .queryParam("issue_type_id", issueV2FilterDto.getIssue_type_id())
                    .queryParam("assignee_id", issueV2FilterDto.getAssignee_id())
                    .queryParam("collaborator_ids", issueV2FilterDto.getCollaborator_ids())
                    .queryParam("program_id", issueV2FilterDto.getProgram_id())
                    .queryParam("state", issueV2FilterDto.getState())
                    .queryParam("priority", issueV2FilterDto.getPriority())
                    .queryParam("search", issueV2FilterDto.getSearch())
                    .queryParam("plan_started_at", issueV2FilterDto.getPlan_started_at())
                    .queryParam("deadline", issueV2FilterDto.getDeadline())
                    .queryParam("created_at", issueV2FilterDto.getCreated_at())
                    .queryParam("page", issueV2FilterDto.getPage())
                    .queryParam("per_page", issueV2FilterDto.getPer_page())
                    .toUriString();

            ResponseEntity<IssueOutDto> postForEntity = restTemplate.getForEntity(URLDecoder.decode(uriString, "UTF-8"), IssueOutDto.class);
            if ((HttpStatus.OK).equals(postForEntity.getStatusCode()) || (HttpStatus.CREATED).equals(postForEntity.getStatusCode())) {
                IssueOutDto body = postForEntity.getBody();
                body.getData().stream().forEach(h -> IssueFormatterConverter(h));
                return body.getData();
            }
            logger.info("gittee queryIssuesByGroup请求失败。返回原始报文:" + postForEntity.getBody());
            return null;
        } catch (Exception e) {
            logger.error("gitee接口queryIssuesByGroup异常:", e);
            throw PlatformException.error("gitee接口queryIssuesByGroup异常");
        }
    }

    public List<IssueExportDto> queryIssueExportList(SearchIssueParamDto params, int weekNo, LocalDate week) {
        try {
            SearchIssueParamDto.IssueV2FilterDto issueV2FilterDto = params.buildIssuseV2FilterDto();
            String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl + "issues/")
                    .queryParam("access_token", accessToken)
                    .queryParam("issue_state_ids", issueV2FilterDto.getIssue_state_ids())
                    .queryParam("issue_type_id", issueV2FilterDto.getIssue_type_id())
                    .queryParam("assignee_id", issueV2FilterDto.getAssignee_id())
                    .queryParam("collaborator_ids", issueV2FilterDto.getCollaborator_ids())
                    .queryParam("program_id", issueV2FilterDto.getProgram_id())
                    .queryParam("state", issueV2FilterDto.getState())
                    .queryParam("priority", issueV2FilterDto.getPriority())
                    .queryParam("search", issueV2FilterDto.getSearch())
                    .queryParam("plan_started_at", issueV2FilterDto.getPlan_started_at())
                    .queryParam("deadline", issueV2FilterDto.getDeadline())
                    .queryParam("created_at", issueV2FilterDto.getCreated_at())
                    .queryParam("page", issueV2FilterDto.getPage())
                    .queryParam("per_page", issueV2FilterDto.getPer_page())
                    .toUriString();

            ResponseEntity<IssueOutDto> postForEntity = restTemplate.getForEntity(URLDecoder.decode(uriString, "UTF-8"), IssueOutDto.class);
            if ((HttpStatus.OK).equals(postForEntity.getStatusCode()) || (HttpStatus.CREATED).equals(postForEntity.getStatusCode())) {
                IssueOutDto body = postForEntity.getBody();
                return body.getData().stream().map(h -> IssueFormatterConverter2Export(h, weekNo, week))
                        .sorted(Comparator.comparing(IssueExportDto::getProgramName))
                        .collect(Collectors.toList());
            }
            logger.info("gittee 周报导出失败。返回原始报文:" + postForEntity.getBody());
            return null;
        } catch (Exception e) {
            logger.error("gitee周报导出异常:", e);
            throw PlatformException.error("gitee周报导出异常");
        }
    }

    public List<IssueOutVo> getGroupListByAssignee(List<IssueBaseDto> issueList) {
        //[{assigneeId:45,children:[]}]
        List<IssueOutVo> result = issueList.stream()
                .filter(h -> Objects.nonNull(h.getAssignee()))
                .collect(Collectors.groupingBy(h -> h.getAssignee().getId()))
                .entrySet()
                .stream()
                .map(entry -> {
                    IssueOutVo item = IssueOutVo.builder()
                            .assigneeId(entry.getKey())
                            .children(entry.getValue()).build();
                    return item;
                }).sorted(
                        Comparator.comparing(IssueOutVo::getAssigneeId)
                ).collect(Collectors.toList());
        return result;
    }


    /**
     * 查询任务分页
     *
     * @param params
     * @return
     */
//    public Page<IssueDto> queryIssues(SearchIssueParamDto params) {
//        params.setPageNo(PageUtil.initPageNo(params.getPageNo()));
//        params.setPageSize(PageUtil.initPageSize(params.getPageSize()));
//        try {
//            SearchIssueParamDto.IssuseFilterDto issuseFilterDto = params.buildIssuseFilterDto(accessToken);
//            HttpEntity<SearchIssueParamDto.IssuseFilterDto> httpEntity = new HttpEntity<>(issuseFilterDto);
//            ResponseEntity<IssueOutDto> postForEntity = restTemplate.postForEntity(postIssuesUrl, httpEntity, IssueOutDto.class);
//
//            if ((HttpStatus.OK).equals(postForEntity.getStatusCode()) || (HttpStatus.CREATED).equals(postForEntity.getStatusCode())) {
//                IssueOutDto body = postForEntity.getBody();
//                Pageable pageable = PageRequest.of(params.getPageNo()-1, params.getPageSize());
//                body.getIssues().stream().forEach(h -> IssueFormatterConverter(h));
//                Page<IssueDto> resultPage = new PageImpl(body.getIssues(), pageable, body.getTotal_count());
//                return resultPage;
//            }
//            logger.info("gittee queryIssues请求失败。返回原始报文:" + postForEntity.getBody());
//            return null;
//        } catch (Exception e) {
//            logger.error("gitee接口queryIssues异常:", e);
//            throw PlatformException.error("gitee接口queryIssues异常");
//        }
//
//    }

    /**
     * 查询人员分页
     *
     * @param params
     * @return
     */
    public Page<GiteeMemberDto> queryMembers(SearchGiteeMemberParamDto params) {
        params.setPageNo(PageUtil.initPageNo(params.getPageNo()));
        params.setPageSize(PageUtil.initPageSize(params.getPageSize()));
        try {
            String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl + "members/")
                    .queryParam("access_token", accessToken)
                    .queryParam("search", params.getSearch())
//                    .queryParam("group_id",params.getGroupId())
//                    .queryParam("role_id",params.getRoleId())
                    .queryParam("page", params.getPageNo())
                    .queryParam("per_page", params.getPageSize())
                    .toUriString();

            ResponseEntity<GiteeMemberOutDto> postForEntity = restTemplate.getForEntity(uriString, GiteeMemberOutDto.class);

            if ((HttpStatus.OK).equals(postForEntity.getStatusCode())) {
                GiteeMemberOutDto body = postForEntity.getBody();
                Pageable pageable = PageRequest.of(params.getPageNo() - 1, params.getPageSize());
                Page<GiteeMemberDto> resultPage = new PageImpl(body.getData(), pageable, body.getTotal_count());
                return resultPage;
            }
            logger.info("gittee queryMemberList请求失败。方法返回原始报文:" + postForEntity.getBody());
            return null;
        } catch (Exception e) {
            logger.error("gitee接口queryMemberList异常:", e);
            throw PlatformException.error("gitee接口queryMemberList异常");
        }
    }

    /**
     * 根据任务ident获取任务详情
     */
    public IssueDetailDto queryIssueByIdent(String ident) {
        if (StringUtils.isBlank(ident)) {
            throw PlatformException.error("任务标识ident不能为空！");
        }
//        Map<String, String> uriVariables = new HashMap<>();
//        uriVariables.put("ident",ident);
        try {
            String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl)
                    .path("issues/" + ident)
                    .queryParam("access_token", accessToken)
                    .queryParam("qt", "ident")
                    .toUriString();
            ResponseEntity<IssueDetailDto> postForEntity = restTemplate.getForEntity(uriString, IssueDetailDto.class);
            if ((HttpStatus.OK).equals(postForEntity.getStatusCode())) {
                IssueDetailDto dto = postForEntity.getBody();
                IssueFormatterConverter(dto);
                return dto;
            }
            logger.info("gittee 查询任务详情queryIssueByIdent请求失败。方法返回原始报文:" + postForEntity.getBody());
            return null;
        } catch (Exception e) {
            logger.error("gitee接口queryIssueByIdent异常:", e);
            throw PlatformException.error("gitee接口queryIssueByIdent异常");
        }
    }

    /**
     * 根据任务id获取附件列表
     *
     * @param id
     * @return
     */
    public List<IssueFileDto> queryIssueFileList(String id) {
        if (StringUtils.isBlank(id)) {
            throw PlatformException.error("任务id不能为空！");
        }
//        Map<String, String> uriVariables = new HashMap<>();
//        uriVariables.put("id",id);
        try {
            String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl)
                    .path("issues/" + id + "/attach_files")
                    .queryParam("access_token", accessToken)
                    .toUriString();
            ParameterizedTypeReference<List<IssueFileDto>> responseType = new ParameterizedTypeReference<List<IssueFileDto>>() {
            };
            ResponseEntity<List<IssueFileDto>> postForEntity = restTemplate.exchange(
                    uriString,
                    HttpMethod.GET,
                    null,
                    responseType
            );

            if ((HttpStatus.OK).equals(postForEntity.getStatusCode())) {
                List<IssueFileDto> body = postForEntity.getBody();
                return body;
            }
            logger.info("gittee 查询任务附件queryIssueFileList请求失败。方法返回原始报文:" + postForEntity.getBody());
            return null;
        } catch (Exception e) {
            logger.error("gitee接口queryIssueFileList异常:", e);
            throw PlatformException.error("gitee接口queryIssueFileList异常");
        }
    }

    public ResponseEntity<byte[]> downLoadIssueFileById(String id) {
        if (StringUtils.isBlank(id)) {
            throw PlatformException.error("附件id不能为空！");
        }
        try {
            String uriString = UriComponentsBuilder.fromHttpUrl(baseUrl)
                    .path("attach_files/" + id)
                    .queryParam("access_token", accessToken)
                    .toUriString();
            ResponseEntity<byte[]> forEntity = restTemplate.getForEntity(uriString, byte[].class);
            if ((HttpStatus.OK).equals(forEntity.getStatusCode())) {
                return forEntity;
            }
            logger.info("gittee任务附件下载downLoadIssueFileById请求失败。");
            return null;
        } catch (Exception e) {
            logger.error("gitee接口downLoadIssueFileById异常:", e);
            throw PlatformException.error("gitee接口downLoadIssueFileById异常");
        }
    }

    /**
     * 将issues item中带时区格式字符串转换为yyyy-mm-dd格式
     *
     * @param h
     * @param <T>
     */
    public <T extends IssueBaseDto> void IssueFormatterConverter(T h) {
        h.setPlan_started_at(DateUtil.getStr_yyyyMMdd2(DateUtil.fromString(h.getPlan_started_at(), timeZoneFormatter)));
        h.setDeadline(DateUtil.getStr_yyyyMMdd2(DateUtil.fromString(h.getDeadline(), timeZoneFormatter)));
        h.setStarted_at(DateUtil.getStr_yyyyMMdd2(DateUtil.fromString(h.getStarted_at(), timeZoneFormatter)));
        h.setFinished_at(DateUtil.getStr_yyyyMMdd2(DateUtil.fromString(h.getFinished_at(), timeZoneFormatter)));
        h.setCreated_at(DateUtil.getStr_yyyyMMdd2(DateUtil.fromString(h.getCreated_at(), timeZoneFormatter)));
        h.setUpdated_at(DateUtil.getStr_yyyyMMdd2(DateUtil.fromString(h.getUpdated_at(), timeZoneFormatter)));
        if (Arrays.binarySearch(closeStateIdArray, h.getIssue_state_id()) >= 0) {
            h.setOverDays(null);
        } else {
            Long overDays = CommonUtils.dateDiff(CommonUtils.getCurrentDateStr(), h.getDeadline());
            h.setOverDays(overDays);
        }
    }

    public IssueExportDto IssueFormatterConverter2Export(IssueBaseDto h, int weekNo, LocalDate week) {
        return IssueExportDto.builder()
                .title(h.getTitle())
                .issueType(Objects.nonNull(h.getIssue_type()) ? h.getIssue_type().getTitle() : Strings.EMPTY)
                .issueState(Objects.nonNull(h.getIssue_state()) ? h.getIssue_state().getTitle() : Strings.EMPTY)
                .programName(Objects.nonNull(programMap.get(h.getProgram_id())) ? programMap.get(h.getProgram_id()) : "未关联项目")
                .week(getWeekDate(weekNo, week))
                .collaborators(CommonUtils.ListJoin2String(
                        Objects.nonNull(h.getCollaborators()) ?
                                h.getCollaborators().stream().map(GiteeMemberDto::getName).collect(Collectors.toList())
                                : null
                ))
                .build();
    }

    public Page<IssueBase> pageIssueBase(PageQueryParamDto param) {
        return issueBaseRepo.findAll(param.getPageable());
    }
}
